﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using StoreTransport.Data;
using StoreTransport.DAO;
using StoreTransport.DTO;

namespace StoreTransport.Business.Services {
    class TransportService : ITransportService {
        private Dictionary<int, List<MarketData>> GetLackProductInMarkets(List<MarketData> markets,
            List<int> RegionIds) {
            Dictionary<int, List<MarketData>> lackOfProductMarkets = new Dictionary<int, List<MarketData>>();
            foreach (MarketData m in markets) {
                if (RegionIds.Contains(m.RegionId)) {
                    foreach (int ProductKindId in m.DicProductKinds.Keys) {
                        ProductKindData p = m.DicProductKinds[ProductKindId];
                        if (p.ComputedMinProducts > 0) {
                            if (!lackOfProductMarkets.ContainsKey(ProductKindId)) {
                                lackOfProductMarkets.Add(ProductKindId, new List<MarketData>());
                            }
                            lackOfProductMarkets[ProductKindId].Add(m);
                        }
                    }
                }
            }
            return lackOfProductMarkets;
        }

        private Dictionary<int, List<MarketData>> GetExcessProductMarkets(List<MarketData> markets,
            List<int> RegionIds) {
            Dictionary<int, List<MarketData>> excessProductMarkets = new Dictionary<int, List<MarketData>>();
            foreach (MarketData m in markets) {
                if (RegionIds.Contains(m.RegionId)) {
                    foreach (int ProductKindId in m.DicProductKinds.Keys) {
                        ProductKindData p = m.DicProductKinds[ProductKindId];
                        if (p.ComputedMaxProducts < 0) {
                            if (!excessProductMarkets.ContainsKey(ProductKindId)) {
                                excessProductMarkets.Add(ProductKindId, new List<MarketData>());
                            }
                            excessProductMarkets[ProductKindId].Add(m);
                        }
                    }
                }
            }
            return excessProductMarkets;
        }

        public TransportResultDto BalanceProductBetweenMarkets(ProductLine pl, List<int> RegionIds,
            StoreTransportDataContext context, List<MarketData> afterAllocationMarkets) {
            List<MarketProductImport> listData = new List<MarketProductImport>();
            if (afterAllocationMarkets.Count == 0) {
                listData = context.MarketProductImports.Where(r => pl.ID == r.ProductLineId
                                        && !(r.StoreAmount == 0 && r.SaleRate == 0)
                                        && (RegionIds.Count <= 0 || RegionIds.Contains(r.RegionId))).ToList();

            } else {
                // update data when allocation before transport
                foreach (MarketData m in afterAllocationMarkets) {
                    foreach (ProductKindData kind in m.DicProductKinds.Values) {
                        foreach (ProductData p in kind.DicProducts.Values) {
                            MarketProductImport rr = new MarketProductImport();
                            rr.MarketId = m.MarketId;
                            rr.ProductId = p.ProductId;
                            rr.ProductKindId = kind.ProductKindId;
                            rr.ProductLineId = pl.ID;
                            rr.StoreAmount = p.ComputedInventory;
                            rr.SaleRate = p.SaleRate;
                            rr.AreaId = m.AreaId;
                            rr.RegionId = m.RegionId;
                            listData.Add(rr);
                        }
                    }
                }
            }
            List<MarketData> markets = Calculator.BuildMarketDto(listData, pl, false, null, null);

            Dictionary<int, List<MarketData>> lackOfProductMarkets = GetLackProductInMarkets(markets, RegionIds);
            Dictionary<int, List<MarketData>> excessProductMarkets = GetExcessProductMarkets(markets, RegionIds);
            TransportResultDto dto = new TransportResultDto();
            foreach (int ProductKindId in lackOfProductMarkets.Keys) {
                List<MarketData> lackMarkets = lackOfProductMarkets[ProductKindId];
                if (excessProductMarkets.ContainsKey(ProductKindId)) {
                    List<MarketData> excessMarkets = excessProductMarkets[ProductKindId];
                    dto.transports.AddRange(transportByArea(lackMarkets, excessMarkets, ProductKindId));
                    dto.transports.AddRange(transportByRegion(lackMarkets, excessMarkets, ProductKindId));
                }
            }
            dto.markets.AddRange(markets);
            return dto;
        }


        private List<TransportData> transportByArea(List<MarketData> lackMarkets,
            List<MarketData> excessMarkets, int ProductKindId) {
            List<TransportData> transportData = new List<TransportData>();
            foreach (MarketData m1 in lackMarkets) {
                foreach (MarketData m2 in excessMarkets) {
                    ProductKindData p1 = m1.DicProductKinds[ProductKindId];
                    if (p1.ComputedMinProducts <= 0) {
                        break;
                    }
                    if (m2.AreaName.Equals(m1.AreaName)) {
                        transportData.AddRange(transport(m1, m2, ProductKindId));
                    }
                }
            }
            return transportData;
        }

        private List<TransportData> transportByRegion(List<MarketData> lackMarkets,
            List<MarketData> excessMarkets, int ProductKindId) {
            List<TransportData> transportData = new List<TransportData>();
            foreach (MarketData m1 in lackMarkets) {
                foreach (MarketData m2 in excessMarkets) {
                    ProductKindData p1 = m1.DicProductKinds[ProductKindId];
                    if (p1.ComputedMinProducts <= 0) {
                        break;
                    }
                    if (m2.RegionName.Equals(m1.RegionName)) {
                        transportData.AddRange(transport(m1, m2, ProductKindId));
                    }
                }
            }
            return transportData;
        }

        private List<TransportData> transport(MarketData m1, MarketData m2, int ProductKindId) {
            List<TransportData> transportData = new List<TransportData>();
            if (!m1.MarketName.Equals(m2.MarketName)) {
                if (!m1.DicProductKinds.ContainsKey(ProductKindId))
                    return transportData;
                ProductKindData p1 = m1.DicProductKinds[ProductKindId];
                ProductKindData p2 = m2.DicProductKinds[ProductKindId];
                Dictionary<string, TransportData> DicTransport = new Dictionary<string, TransportData>();
                if (p1.ComputedMinProducts > 0 && p2.ComputedMaxProducts < 0) {
                    int Amount;
                    if (Math.Abs(p1.ComputedMinProducts) <= Math.Abs(p2.ComputedMaxProducts)) {
                        // ok enough product from p2 to p1
                        Amount = p1.ComputedMinProducts;
                        p2.ComputedMaxProducts = p1.ComputedMinProducts + p2.ComputedMaxProducts;
                        p1.ComputedMinProducts = 0;
                    } else {
                        // need more...
                        Amount = Math.Abs(p2.ComputedMaxProducts);
                        p1.ComputedMinProducts = p1.ComputedMinProducts + p2.ComputedMaxProducts;
                        p2.ComputedMaxProducts = 0;
                    }
                    p1.Amount += Amount;
                    p2.Amount -= Amount;
                    List<ProductData> productOfMarket2 = p2.GetPriorityList(false);
                    foreach (ProductData product in productOfMarket2) {
                        TransportData t = new TransportData();
                        t.marketIdFrom = m2.MarketId;
                        t.marketIdTo = m1.MarketId;
                        t.marketNameFrom = m2.MarketName;
                        t.marketNameTo = m1.MarketName;
                        t.productCode = product.ProductCode;
                        t.productName = product.ProductName;
                        t.areaName = m2.AreaName;
                        t.regionName = m2.RegionName;
                        t.productKindName = p2.ProductKindName;
                        t.productLineName = product.ProductLineName;
                        t.productKindId = p2.ProductKindId;
                        if (product.ComputedInventory >= Amount) {
                            t.numberTransport = Amount;
                            product.ComputedInventory -= Amount;
                            Amount = 0;
                        } else {
                            t.numberTransport = product.ComputedInventory;
                            Amount = Amount - product.ComputedInventory;
                            product.ComputedInventory = 0;
                        }
                        transportData.Add(t);
                        if (Amount <= 0) {
                            break;
                        }
                    }
                }
            }
            return transportData;
        }
    }
}
